package thesis.bean;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.validator.ValidatorException;

import org.primefaces.model.map.DefaultMapModel;
import org.primefaces.model.map.LatLng;
import org.primefaces.model.map.MapModel;
import org.primefaces.model.map.Marker;
import org.primefaces.model.map.Polyline;
import org.springframework.beans.factory.config.MapFactoryBean;

import thesis.database.Customer;
import thesis.database.Route;
import thesis.database.Service;
import thesis.database.Taxi;
import thesis.javaclass.MapFunction;
import thesis.javaclass.SystemModel;
import thesis.javaclass.TempAccept;
import thesis.javaclass.UseService;
import thesis.javaclass.StatusCode;

@ManagedBean(name = "taxiService")
@SessionScoped
public class TaxiServiceBean implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private static final String HOME_ADDRESS = "home";
	private static final String OFFICE_ADDRESS = "office";
	private static final String NEWEST_ADDRESS = "newest";

	private String phoneNumber;
	private Customer customer;
	private int numberOfSeat = 4;
	private String newAddress;
	private String selectAddress;
	private String address;
	private ArrayList<String> info;
	private ArrayList<TempAccept> acceptedTaxies = new ArrayList<TempAccept>();
	private int lengthOfList;

	private MapModel map;
	private String mapCenter = "10.762, 106.685";
	private int zoomLevel = 15;

	public int getZoomLevel() {
		return zoomLevel;
	}

	public void setZoomLevel(int zoomLevel) {
		this.zoomLevel = zoomLevel;
	}

	public enum Seat {
		SEAT_FOUR(4), SEAT_SEVEN(7);

		private final int id;

		Seat(int id) {
			this.id = id;
		}

		public int getValue() {
			return id;
		}
	};

	private List<UseService> useServices = new ArrayList<UseService>();
	private Service service;
	private Service selectedService;

	private boolean newCustomer;
	private boolean oldCustomer;

	private boolean addEnable = true;
	private boolean updateEnable = true;

	public TaxiServiceBean() {
		map = new DefaultMapModel();
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Service getSelectedService() {
		return selectedService;
	}

	public void setSelectedService(Service selectedService) {
		this.selectedService = selectedService;
	}

	public ArrayList<TempAccept> getAcceptedTaxies() {
		// System.out.println(acceptedTaxies.size());
		return acceptedTaxies;
	}

	public MapModel getMap() {
		return map;
	}

	public void setMap(MapModel map) {
		this.map = map;
	}

	public String getMapCenter() {
		return mapCenter;
	}

	public Service getService() {
		return service;
	}

	public void setService(Service service) {
		this.service = service;
	}

	public List<UseService> getUseServices() {
		return useServices;
	}

	public boolean isAddEnable() {
		return addEnable;
	}

	public void setAddEnable(boolean addEnable) {
		this.addEnable = addEnable;
	}

	public boolean isUpdateEnable() {
		return updateEnable;
	}

	public void setUpdateEnable(boolean updateEnable) {
		this.updateEnable = updateEnable;
	}

	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}

	public String getPhoneNumber() {
		return phoneNumber;
	}

	public Customer getCustomer() {
		return customer;
	}

	public boolean isNewCustomer() {
		return newCustomer;
	}

	public boolean isOldCustomer() {
		return oldCustomer;
	}

	public void setCustomer(Customer customer) {
		this.customer = customer;
	}

	public int getNumberOfSeat() {
		return numberOfSeat;
	}

	public void setNumberOfSeat(int numberOfSeat) {
		this.numberOfSeat = numberOfSeat;
	}

	public void setNewAddress(String newAddress) {
		this.newAddress = newAddress;
	}

	public String getSelectAddress() {
		return selectAddress;
	}

	public void setSelectAddress(String selectAddress) {
		this.selectAddress = selectAddress;
	}

	public String getNewAddress() {
		return newAddress;
	}

	public ArrayList<String> getInfo() {
		return info;
	}

	public void increment() {
		System.out.println(0);
	}

	public void findCustomer() {
		this.customer = SystemModel.getCustomerByPhoneNumber(this.phoneNumber);
		// this.newAddress = ""; // because of session scope
		if (customer == null) {
			newCustomer = true;
			oldCustomer = false;
			customer = new Customer();
			customer.setNewestPhone(this.phoneNumber);

			autoSelect();
		} else {
			oldCustomer = true;
			newCustomer = false;

			autoSelect();
		}

		acceptedTaxies = new ArrayList<TempAccept>();
		addEnable = true;
		updateEnable = true;
	}

	public boolean validateRequest() {
		FacesContext context = FacesContext.getCurrentInstance();

		if ((address.equals(HOME_ADDRESS) && customer.getHomeAddress().trim() == "")
				|| (address.equals(OFFICE_ADDRESS) && customer
						.getOfficeAddress().trim() == "")
				|| (address.equals(NEWEST_ADDRESS) && customer
						.getNewestAddress().trim() == "")) {
			context.addMessage(null, new FacesMessage(
					"Choosen an empty address, try again!"));
			return false;
		} else if (address.trim() == "" || address == null) {
			context.addMessage(null, new FacesMessage(
					"Please choose an address!"));
			return false;
		}

		return true;
	}

	public void autoSelect() {
		if (this.customer == null)
			address = NEWEST_ADDRESS;
		else if (this.customer != null) {
			if (!this.customer.getHomeAddress().trim().equals(""))
				address = HOME_ADDRESS;
			else if (!this.customer.getOfficeAddress().trim().equals(""))
				address = OFFICE_ADDRESS;
			else {
				address = NEWEST_ADDRESS;
			}
		}
	}

	public void update() {
		if (!validateRequest())
			return;
		FacesContext context = FacesContext.getCurrentInstance();

		boolean ok = SystemModel.updateCustomer(this.customer);

		if (address.equals(HOME_ADDRESS))
			selectAddress = customer.getHomeAddress();
		else if (address.equals(OFFICE_ADDRESS))
			selectAddress = customer.getOfficeAddress();
		else if (address.equals(NEWEST_ADDRESS))
			selectAddress = customer.getNewestAddress();

		if (ok) {
			updateEnable = false;
			findTaxi();
		} else
			context.addMessage(null, new FacesMessage("Fail Update"));
	}

	public void add() {
		if (!validateRequest())
			return;
		FacesContext context = FacesContext.getCurrentInstance();

		int ok = SystemModel.creatCustomerReturnId(customer);

		if (ok > 0) {
			addEnable = false;
			customer = SystemModel.getCustomer(ok);

			if (address.equals(HOME_ADDRESS))
				selectAddress = customer.getHomeAddress();
			else if (address.equals(OFFICE_ADDRESS))
				selectAddress = customer.getOfficeAddress();
			else if (address.equals(NEWEST_ADDRESS))
				selectAddress = customer.getNewestAddress();

			findTaxi();
		} else
			context.addMessage(null, new FacesMessage("Fail Insert"));
	}

	public void reback() {
		updateEnable = true;
	}

	public void validatePhoneNumber(FacesContext fc, UIComponent c, Object value) {
		String temp = (String) value;
		if (!(temp.matches("[0-9]{10}") || temp.matches("[0-9]{11}")))
			throw new ValidatorException(new FacesMessage(
					"Phone number must have 10-11 numbers"));
	}

	/***********************************************************
	 ********************************************************** 
	 public String findTaxi() { String address = ""; // submit from
	 * new-customer if (this.customer.getCustomerId() == 0) {
	 * this.customer.setOfficePhone(this.phoneNumber);
	 * SystemModel.addCustomer(this.customer); address =
	 * this.customer.getHomeAddress(); } // submit from old-customer else { //
	 * enter newAddress if (!this.newAddress.trim().equals("")) {
	 * SystemModel.updateCustomer(this.customer.getCustomerId(), newAddress);
	 * address = this.newAddress; } // selected from old address else { if
	 * (this.selectAddress.equals("home")) { address =
	 * this.customer.getHomeAddress(); } else if
	 * (this.selectAddress.equals("office")) { address =
	 * this.customer.getOfficeAddress(); } else if
	 * (this.selectAddress.equals("lastes")) { address =
	 * this.customer.getNewestAddress(); } } } this.info =
	 * SystemModel.findRelavantTaxi(address, numberOfSeat, this.phoneNumber);
	 * return "result-page"; // show info and customer }
	 ************************************************************ 
	 *************************************************************/

	public int getLengthOfList() {
		if (acceptedTaxies == null)
			return 0;
		lengthOfList = acceptedTaxies.size();
		return lengthOfList;
	}

	public void findTaxi() {
		FacesContext context = FacesContext.getCurrentInstance();
		acceptedTaxies = new ArrayList<TempAccept>();
		String extraAddress = new String(selectAddress);
		System.out.println(selectAddress);
		System.out.println(numberOfSeat);

		String selected = SystemModel.findRelavantTaxi(acceptedTaxies,
				selectAddress, numberOfSeat, phoneNumber).get(0);

		if (acceptedTaxies.size() == 0) {
			context.addMessage(null, new FacesMessage(
					"Couldn't find coordinates from selected address"));
			return;
		}
		Date date = new Date();
		context.addMessage(null, new FacesMessage("Progess completed for "
				+ phoneNumber));

		Taxi taxi = SystemModel.getTaxiByNumberPlate(selected).get(0);
		ArrayList<Double> a = null;
		try {
			a = MapFunction.getCoordinate(extraAddress);
		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println(extraAddress);

		service = new Service();
		service.setAcceptTime(new Timestamp(date.getTime()));
		service.setBeginAddress(extraAddress);
		service.setTaxiId(taxi.getTaxiId());
		service.setCustomerId(customer.getCustomerId());
		service.setStatus(1);
		service.setBeginLatitude(String.valueOf(a.get(0)));
		service.setBeginLongitude(String.valueOf(a.get(1)));

		int id = SystemModel.createServiceReturnId(service);

		useServices.add(0,
				new UseService(taxi.getTaxiId(), customer.getCustomerId(), id));
	}

	public void mapView(ActionEvent event) {
		map = new DefaultMapModel();

		Object temp = event.getComponent().getAttributes().get("idmap");
		int id = Integer.parseInt(temp.toString());

		System.out.print(id + "\n");

		Service serviceTemp = SystemModel.getServiceById(id);
		Taxi taxi = SystemModel.getTaxi(serviceTemp.getTaxiId());

		try {
			Polyline polyline = new Polyline();
			ArrayList<Double> latList1 = new ArrayList<Double>();
			ArrayList<Double> lngList1 = new ArrayList<Double>();
			boolean flag = false;

			if (serviceTemp.getStatus() == StatusCode.ACCEPT_STATUS
					|| serviceTemp.getStatus() == StatusCode.CAN_NOT_PICKUP_STATUS) {
				String taxiPos = MapFunction.getLocation(
						Double.parseDouble(taxi.getNewestLatitude()),
						Double.parseDouble(taxi.getNewestLongitude()));

				String customerPos = MapFunction.getLocation(
						Double.parseDouble(serviceTemp.getBeginLatitude()),
						Double.parseDouble(serviceTemp.getBeginLongitude()));

				ArrayList<ArrayList<String>> directions = MapFunction
						.getDirection(taxiPos, customerPos);
				ArrayList<String> latList = directions.get(0);
				ArrayList<String> lngList = directions.get(1);

				if (latList.size() < 2 || lngList.size() < 2) {
					flag = true;
				}

				if (latList.size() != lngList.size()) {
					System.out.println("There's something wrong occured!");
					return;
				}

				LatLng llTaxi = new LatLng(Double.parseDouble(latList.get(0)),
						Double.parseDouble(lngList.get(0)));

				map.addOverlay(new Marker(llTaxi, "Taxi",
						"images\\Taxi-icon1.png",
						"http://i276.photobucket.com/albums/kk3/lyhung009/Thesis/taxi-1.png"));

				polyline.getPaths().add(llTaxi);

				for (int i = 1; i < latList.size() - 1; i++) {
					LatLng llRoute = new LatLng(Double.parseDouble(latList
							.get(i)), Double.parseDouble(lngList.get(i)));

					map.addOverlay(new Marker(llRoute, "Route", "",
							"http://labs.google.com/ridefinder/images/mm_20_purple.png"));
					polyline.getPaths().add(llRoute);
				}

				LatLng llCustomer = new LatLng(Double.parseDouble(latList
						.get(latList.size() - 1)), Double.parseDouble(lngList
						.get(lngList.size() - 1)));

				map.addOverlay(new Marker(llCustomer, "Customer", "",
						"http://i276.photobucket.com/albums/kk3/lyhung009/Thesis/customer.png"));
				polyline.getPaths().add(llCustomer);

				for (String lat : latList) {
					latList1.add(Double.parseDouble(lat));
				}

				for (String lng : lngList) {
					lngList1.add(Double.parseDouble(lng));
				}
			} else if (serviceTemp.getStatus() == StatusCode.PICKED_UP_STATUS) {
				List<Route> routes = SystemModel.getRouteById(serviceTemp
						.getTaxiId(), serviceTemp.getBeginTime().toString(),
						new Timestamp(new Date().getTime()).toString());

				if (routes.size() < 2) {
					flag = true;
					if (routes.size() == 1)
						mapCenter = routes.get(0).getLatitude() + ", "
								+ routes.get(0).getLongitude();
				}

				for (Route route : routes) {
					LatLng r = new LatLng(Double.parseDouble(route
							.getLatitude()), Double.parseDouble(route
							.getLongitude()));

					latList1.add(r.getLat());
					lngList1.add(r.getLng());

					map.addOverlay(new Marker(r, "Route", "",
							"http://labs.google.com/ridefinder/images/mm_20_purple.png"));
					polyline.getPaths().add(r);

				}
			} else if (serviceTemp.getStatus() == StatusCode.FINISH_STATUS) {
				List<Route> routes = SystemModel.getRouteById(serviceTemp
						.getTaxiId(), serviceTemp.getBeginTime().toString(),
						serviceTemp.getEndTime().toString());

				if (routes.size() < 2) {
					flag = true;
					if (routes.size() == 1)
						mapCenter = routes.get(0).getLatitude() + ", "
								+ routes.get(0).getLongitude();
				}

				for (Route route : routes) {
					LatLng r = new LatLng(Double.parseDouble(route
							.getLatitude()), Double.parseDouble(route
							.getLongitude()));

					latList1.add(r.getLat());
					lngList1.add(r.getLng());

					map.addOverlay(new Marker(r, "Route", "",
							"http://labs.google.com/ridefinder/images/mm_20_purple.png"));
					polyline.getPaths().add(r);
				}
			}

			ArrayList<Double> results = MapFunction.getZoomLevel(latList1,
					lngList1);

			if (flag == false) {
				zoomLevel = results.get(0).intValue();
				mapCenter = results.get(1) + ", " + results.get(2);
			}
			System.out.println(zoomLevel);

			polyline.setStrokeWeight(8);
			polyline.setStrokeColor("0000ff");
			polyline.setStrokeOpacity(0.8);

			map.addOverlay(polyline);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public int getIdCode(FacesContext context) {
		Map<String, String> params = context.getExternalContext()
				.getRequestParameterMap();

		System.out.print(params.get("idmap"));
		return Integer.parseInt(params.get("idmap"));
	}
}
